{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| default_exp fastapp"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# fastapp\n",
    "> The `fast_app` convenience wrapper"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Usage can be summarized as:\n",
    "\n",
    "```python\n",
    "from fasthtml.common import *\n",
    "\n",
    "app, rt = fast_app()\n",
    "\n",
    "@rt('/')\n",
    "def get(): return Titled(\"A demo of fast_app()@\")\n",
    "\n",
    "serve()\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "from __future__ import annotations\n",
    "\n",
    "import inspect,uvicorn\n",
    "from fastcore.utils import *\n",
    "from fastlite import *\n",
    "from fasthtml.basics import *\n",
    "from fasthtml.pico import *\n",
    "from fasthtml.starlette import *\n",
    "from fasthtml.live_reload import FastHTMLWithLiveReload"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def _get_tbl(dt, nm, schema):\n",
    "    render = schema.pop('render', None)\n",
    "    tbl = dt[nm]\n",
    "    if tbl not in dt: tbl.create(**schema)\n",
    "    else: tbl.create(**schema, transform=True)\n",
    "    dc = tbl.dataclass()\n",
    "    if render: dc.__ft__ = render\n",
    "    return tbl,dc"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def _app_factory(*args, **kwargs) -> FastHTML | FastHTMLWithLiveReload:\n",
    "    \"Creates a FastHTML or FastHTMLWithLiveReload app instance\"\n",
    "    if kwargs.pop('live', False): return FastHTMLWithLiveReload(*args, **kwargs)\n",
    "    kwargs.pop('reload_attempts', None)\n",
    "    kwargs.pop('reload_interval', None)\n",
    "    return FastHTML(*args, **kwargs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def fast_app(\n",
    "        db_file:Optional[str]=None, # Database file name, if needed\n",
    "        render:Optional[callable]=None, # Function used to render default database class\n",
    "        hdrs:Optional[tuple]=None, # Additional FT elements to add to <HEAD>\n",
    "        ftrs:Optional[tuple]=None, # Additional FT elements to add to end of <BODY>\n",
    "        tbls:Optional[dict]=None, # Experimental mapping from DB table names to dict table definitions\n",
    "        before:Optional[tuple]|Beforeware=None, # Functions to call prior to calling handler\n",
    "        middleware:Optional[tuple]=None, # Standard Starlette middleware\n",
    "        live:bool=False, # Enable live reloading\n",
    "        debug:bool=False, # Passed to Starlette, indicating if debug tracebacks should be returned on errors\n",
    "        routes:Optional[tuple]=None, # Passed to Starlette\n",
    "        exception_handlers:Optional[dict]=None, # Passed to Starlette\n",
    "        on_startup:Optional[callable]=None, # Passed to Starlette\n",
    "        on_shutdown:Optional[callable]=None, # Passed to Starlette\n",
    "        lifespan:Optional[callable]=None, # Passed to Starlette\n",
    "        default_hdrs=True, # Include default FastHTML headers such as HTMX script?\n",
    "        pico:Optional[bool]=None, # Include PicoCSS header?\n",
    "        surreal:Optional[bool]=True, # Include surreal.js/scope headers?\n",
    "        htmx:Optional[bool]=True, # Include HTMX header?\n",
    "        ws_hdr:bool=False, # Include HTMX websocket extension header?\n",
    "        secret_key:Optional[str]=None, # Signing key for sessions\n",
    "        key_fname:str='.sesskey', # Session cookie signing key file name\n",
    "        session_cookie:str='session_', # Session cookie name\n",
    "        max_age:int=365*24*3600, # Session cookie expiry time\n",
    "        sess_path:str='/', # Session cookie path\n",
    "        same_site:str='lax', # Session cookie same site policy\n",
    "        sess_https_only:bool=False, # Session cookie HTTPS only?\n",
    "        sess_domain:Optional[str]=None, # Session cookie domain\n",
    "        htmlkw:Optional[dict]=None, # Attrs to add to the HTML tag\n",
    "        bodykw:Optional[dict]=None, # Attrs to add to the Body tag\n",
    "        reload_attempts:Optional[int]=1, # Number of reload attempts when live reloading\n",
    "        reload_interval:Optional[int]=1000, # Time between reload attempts in ms\n",
    "        **kwargs)->Any:\n",
    "    \"Create a FastHTML or FastHTMLWithLiveReload app.\"\n",
    "    h = (picolink,) if pico or (pico is None and default_hdrs) else ()\n",
    "    if hdrs: h += tuple(hdrs)\n",
    "\n",
    "    app = _app_factory(hdrs=h, ftrs=ftrs, before=before, middleware=middleware, live=live, debug=debug, routes=routes, exception_handlers=exception_handlers,\n",
    "                  on_startup=on_startup, on_shutdown=on_shutdown, lifespan=lifespan, default_hdrs=default_hdrs, secret_key=secret_key,\n",
    "                  session_cookie=session_cookie, max_age=max_age, sess_path=sess_path, same_site=same_site, sess_https_only=sess_https_only,\n",
    "                  sess_domain=sess_domain, key_fname=key_fname, ws_hdr=ws_hdr, surreal=surreal, htmx=htmx, htmlkw=htmlkw,\n",
    "                  reload_attempts=reload_attempts, reload_interval=reload_interval, **(bodykw or {}))\n",
    "\n",
    "    @app.route(\"/{fname:path}.{ext:static}\")\n",
    "    async def get(fname:str, ext:str): return FileResponse(f'{fname}.{ext}')\n",
    "    if not db_file: return app,app.route\n",
    "\n",
    "    db = database(db_file)\n",
    "    if not tbls: tbls={}\n",
    "    if kwargs:\n",
    "        if isinstance(first(kwargs.values()), dict): tbls = kwargs\n",
    "        else:\n",
    "            kwargs['render'] = render\n",
    "            tbls['items'] = kwargs\n",
    "    dbtbls = [_get_tbl(db.t, k, v) for k,v in tbls.items()]\n",
    "    if len(dbtbls)==1: dbtbls=dbtbls[0]\n",
    "    return app,app.route,*dbtbls"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#| export\n",
    "def ContainerX(*cs, **kwargs): return Main(*cs, **kwargs, cls='container', hx_push_url='true', hx_swap_oob='true', id='main')\n",
    "def PageX(title, *con): return Title(title), ContainerX(H1(title), *con)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "python3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
